package edu.gatech.cs4235.group3.action;

import edu.gatech.cs4235.group3.domain.Message;
import edu.gatech.cs4235.group3.exception.OneTimePadException;

/**
 * A CryptAction holds the necessary procedures for encrypting or decrypting an message.
 *
 * @author Jeffrey Starker
 *
 */
public abstract class OnePadCryptAction implements Action {
	
	private final String listOfCryptFiles;
	
	private final String listOfPadFiles;
	
	/**
	 * Creates a OnePadCryptAction with the following parameters.
	 *
	 * @param listOfCryptFiles A comma separated list of crypt files.
	 * @param listOfPadFiles A comma separated list of pad files.
	 */
	public OnePadCryptAction(final String listOfCryptFiles, final String listOfPadFiles) {
		this.listOfCryptFiles = listOfCryptFiles;
		this.listOfPadFiles = listOfPadFiles;
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	public void performAction() throws OneTimePadException {
		
		validateCryptAction();
		
		String[] listOfCryptFilesArray = listOfCryptFiles.split(",");
		String[] listOfPadFilesArray = listOfPadFiles.split(",");
		
		Message padFile = null;
		
		for (int i = 0; i < listOfCryptFilesArray.length; i++) {
			
			String fileName = listOfCryptFilesArray[i];
			
			Message cryptFile = new Message(fileName);
			
			// Use the same pad file if we only have one, otherwise, use different ones.
			if (listOfPadFilesArray.length == 1) {
				if (padFile == null) {
					padFile = new Message(listOfPadFilesArray[0]);
				}
			} else {
				padFile = new Message(listOfPadFilesArray[i]);
			}
			
			Message saveFile = new Message();
			
			char[][] cryptFileTxt = cryptFile.getText();
			char[][] padFileTxt = padFile.getText();
			char[][] saveFileTxt = saveFile.getText();
			
			String format = String.format("{0}ing file: {1}", this.getCryptType(),
										   listOfCryptFilesArray[i]);
			System.out.println(format); // Let the user know that we are crypting a file.
			
			for (int row = 0; row < saveFileTxt.length; row ++) {
				for (int col = 0; col < saveFileTxt[0].length; col++) {
					saveFileTxt[row][col] = this.crypt(cryptFileTxt[row][col], padFileTxt[row][col]);
				}
			}
			
			saveFile.saveMessage(this.getCryptType() + fileName);
		}
	}
	
	/**
	 * Each subclass must override this method which performs the One-Time pad crypt action.
	 *
	 */
	protected abstract char crypt(final char cryptValue, final char padValue);
	
	/**
	 * This is function is a common function for validating whether or not to perform the crypt action in the first place.
	 * This method checks to see if we have a listOfPadFiles and whether or not there are the same number of pad files and
	 * files we have to decrypt or encrypt.
	 * 
	 * @throws OneTimePadException If there is a validation error that causes us to cancel our action.
	 */
	private void validateCryptAction() throws OneTimePadException {
		
		String[] listOfCryptFilesArray = listOfCryptFiles.split(",");
		String[] listOfPadFilesArray = null;
		
		// If we have no pad files, then that is an error.
		if (listOfPadFiles == null) {
			OneTimePadException exception = new OneTimePadException("noPadFilesSpecified", this.getCryptType());
			
			throw exception;
		} else {
			listOfPadFilesArray = listOfPadFiles.split(",");
			
			// If we have one file, then we can just use that to encrypt/decrypt.
			if (listOfPadFilesArray.length == 1) {
				return;
			}
			
			// If we have don't have the same number of files then this is an error.
			if (listOfPadFilesArray.length != listOfCryptFilesArray.length) {
				OneTimePadException exception = new OneTimePadException("numberOfPadFilesMismatch", this.getCryptType());
				
				throw exception;
			}
		}
		
		// If we make it here then we ok to continue running the action.
	}
	
	/**
	 * Used in the validation crypt action. This simply returns the type of Crypt action this is.
	 *
	 * @return The name of the crypt type.
	 */
	public abstract String getCryptType();
	
	public String getListOfCryptFiles() {
		return this.listOfCryptFiles;
	}
	
	public String getListOfPadFiles() {
		return this.listOfPadFiles;
	}
	

}
